
#region Using Statements

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Text;
    using EnvDTE;
    using Microsoft.VisualStudio;
    using Microsoft.VisualStudio.Shell.Interop;
    using Microsoft.Xna.Framework.Content;

#endregion

namespace HumanElement.Xna.Construct.Construct.GameComponents.Design
{
    public class DesignTimeContentManager : ContentManager
    {
        private readonly Project project;

        /// <summary>
        /// Initializes a new instance of the <see cref="DesignTimeContentManager"/> class.
        /// </summary>
        /// <param name="serviceProvider">The service provider.</param>
        /// <param name="project">The project.</param>
        public DesignTimeContentManager(IServiceProvider serviceProvider, Project project)
            : base(serviceProvider)
        {
            if (project == null) throw new ArgumentNullException("project");

            this.project = project;
        }

        /// <summary>
        /// Opens a stream for reading the specified asset. Derived classes can replace this to implement pack files or asset compression.
        /// </summary>
        /// <param name="assetName">The name of the asset being read.</param>
        /// <returns>The opened stream.</returns>
        protected override Stream OpenStream(string assetName)
        {
            var designTimeAssetName = GetDesignTimeAssetName(assetName);

            if (designTimeAssetName == assetName)
            {
                // The asset wasn't found.
                throw new ContentLoadException(
                    String.Format(
                        "Could not find asset '{0}'. Please ensure that the item has built successfully before loading it into the designer.",
                        assetName));
            }

            return base.OpenStream(designTimeAssetName);
        }

        /// <summary>
        /// Gets the name of the design time asset.
        /// </summary>
        /// <param name="assetName">Name of the asset.</param>
        /// <returns></returns>
        private string GetDesignTimeAssetName(string assetName)
        {
            IVsHierarchy hierarchy = Microsoft.VisualStudio.Shell.GetHierarchyForProject(project);
            IList<IVsHierarchy> contentHierarchies = DesignerUtilities.GetNestedContentProjects(hierarchy);

            var projectDirectory = project.Properties.Item("FullPath").Value as string;
            var outputPathProperty = project.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath").Value as string;

            string outputDirectory = Path.Combine(projectDirectory, outputPathProperty);

            var possibleNames = new List<string>(contentHierarchies.Count);

            foreach (var contentHierarchy in contentHierarchies)
            {
                var buildProperties = (IVsBuildPropertyStorage) contentHierarchy;
                string contentRootDirectory;
                if (
                    ErrorHandler.Succeeded(buildProperties.GetPropertyValue("ContentRootDirectory", String.Empty,
                                                                            (uint) _PersistStorageType.PST_PROJECT_FILE,
                                                                            out contentRootDirectory)))
                {
                    string possibleName = Path.Combine(contentRootDirectory, assetName);
                    possibleName = Path.Combine(outputDirectory, possibleName);
                    if (File.Exists(possibleName + ".xnb"))
                    {
                        possibleNames.Add(possibleName);
                    }
                }
            }
            if (possibleNames.Count == 1)
            {
                return possibleNames[0];
            }
            return assetName;
        }
    }
}